This document describes how to create gadgets that can be easily localized for an international audience.
Introduction
The gadgets API gives you an easy way to make your gadget available to an international audience. It’s simple: all you have to do is structure your gadget to so that user-visible text (any text that should be translated) is in message bundles that are separate from your gadget. Then 3rd party translators translate your strings and create new locale-specific message bundles, which you add to your gadget spec. Thus your gadget becomes available to a new group of users.
For a discussion on working with non-English gadgets, see Non-English Gadgets.
Basic Concepts
This document refers to internationalization (i18n) and localization (l10n):
- I18n means structuring applications in a way that makes it possible for them to be localized.
- L10n means making an application work for a particular market, such as French-speaking Canadians.
For the purposes of this document, a user’s “localization profile” consists of two components: a country (based on the user’s domain), and a preferred user interface language (see Testing for details).
Encoding for Internationalization
The gadgets API supports UTF-8 encodings for XML attribute
values and in the <Content>
section.
When your content type is set to html
,
UTF-8 is explicitly set as the default encoding. You should
not try to set this yourself.
Example
The example gadget used in this document displays the message “Hello World,” shown here in Chinese:
In this example, the user preferences drop-down menu lets users specify a font color for the “Hello World” message. The color names that appear in the menu are defined in external files (message bundles). The language in which the menu is displayed depends on the user’s country/language profile. If a user with an English profile runs this gadget, the text appears in English. Here is what the English version of the example looks like with the drop-down userprefs menu displayed:
You can see the gadget spec for Hello World here.
Creating a Localized Gadget
To create a localized gadget, the basic steps are as follows:
- Write the gadget.
- Extract any text that needs to be translated into message bundles.
- Find someone who can help you translate the gadget.
- The translator creates a new message bundle for a particular locale. This could be per language (French), or per language/country (French/Canada, French/France). Note that non-English message bundles must be saved in UTF-8 format.
- The translator sends you a URL to the new bundle.
- Modify your gadget spec to use the message bundle.
- If your gadget is in the Content Directory, wait 1-2 weeks for the crawler and pipeline to find your updated gadget and make it available in the new market.
Message Bundles
The gadgets i18n strategy centers around message bundles. Message bundles are XML files that contain the translated strings for a given locale. Each string is identified by a unique name that is the same across bundles.
Message bundles can be hosted at any URL, and can be shared between applications. Message bundles contain only one locale.
Message bundles have the following format:
<messagebundle> <msg name="hello_world"> Hello World. </msg> <msg name="color">Color</msg> <msg name="red">Red</msg> <msg name="green">Green</msg> <msg name="blue">Blue</msg> <msg name="gray">Gray</msg> <msg name="purple">Purple</msg> <msg name="black">Black</msg> </messagebundle>
The possible fields you can include in a message bundle file are as follows:
- messagebundle: A group of messages for translation.
- msg: A single piece of translatable content, usually a label, sentence, or even paragraph.
- msg name: A human-readable ID for each message.
Naming Conventions
By convention, message bundles are named as follows:
<language>_<country>.xml
Where there is no language- or country-specific value, the convention is to use “ALL.” For example, the file de_ALL.xml applies to all German speakers, regardless of their country. The message bundle ALL_ALL.xml is the file that is used by default.
Best Practices
There is no strict requirement to follow the message bundle naming
conventions. The gadget uses the lang
and country
attributes
of <Locale>
to
identify the correct message bundle file for the user’s profile.
It does not use the name of the message bundle itself.
However, even though the gadgets API gives you a lot of flexibility, we recommend that you follow these guidelines:
- Always provide an ALL_ALL.xml message bundle for default message handling.
- Specify one locale per language. That is, use fr_ALL, de_ALL, and so on.
- If you need to do locale-specific overrides, use lang-country message bundles. For example, if you want to provide a Canadian English message bundle to override en_ALL, use en_CA, not ALL_CA.
Using Message Bundles in a Gadget
You use the <Locale>
tag (nested inside of the <ModulePrefs>
section)
to list the message bundles used by your gadget. For example:
<ModulePrefs title="i18n Example"> <Locale messages="http://doc.examples.googlepages.com/ALL_ALL.xml"/> <Locale lang="de" messages="http://doc.examples.googlepages.com/de_ALL.xml"/> <Locale lang="zh-cn" messages="http://doc.examples.googlepages.com/zh_cn_ALL.xml"/> <Locale lang="fr" messages="http://doc.examples.googlepages.com/fr_ALL.xml"/> <Locale lang="ja" messages="http://doc.examples.googlepages.com/ja_ALL.xml"/> <Locale lang="es" messages="http://doc.examples.googlepages.com/es_ALL.xml"/> <Locale lang="it" messages="http://doc.examples.googlepages.com/it_ALL.xml"/> <Locale lang="ru" messages="http://doc.examples.googlepages.com/ru_ALL.xml"/> </ModulePrefs>
<Locale>
can have the following attributes:
-
messages
: A message bundle that is accessed through a URL. -
lang
: The language the message bundle strings have been translated into. -
country
: There aren’t examples of this in the “Hello World” gadget spec, but you can also specify a country attribute. For example, this message bundle is tailored to French-speaking Canadian users:
<Locale lang="fr" country="CA" messages="http://example.com/fr_CA.xml"/>
At run-time, the gadget uses the message bundle that most closely matches the user’s profile.
The valid values for language are ISO639-1 two-digit language codes, and for country they are ISO 3166-1 alpha-2 codes.
Inline Message Bundles
Instead of storing message bundles in an external file, as shown above, you can put them inline in your gadget. For example, this variation on the "Hello World" sample gadget defines inline message bundles for "all" and "es" (Spanish):
<Module>
<ModulePrefs title="i18n Example"> <Locale lang="all"> <msg name="hello_world">Hello World.</msg> <msg name="color">Color</msg> <msg name="red">Red</msg> <msg name="green">Green</msg> <msg name="blue">Blue</msg> <msg name="gray">Gray</msg> <msg name="purple">Purple</msg> <msg name="black">Black</msg> </Locale> <Locale lang="es"> <msg name="hello_world">Hola Mundo.</msg> <msg name="color">Color</msg> <msg name="red">Rojo</msg> <msg name="green">Verde</msg> <msg name="blue">Azul</msg> <msg name="gray">Gris</msg> <msg name="purple">Púrpura</msg> <msg name="black">Negro</msg> </Locale>
</ModulePrefs> ... </Module>
Message Fallback
Message bundles have a fallback feature. With message fallback, the gadget tries to use the message bundle that most closely matches the user’s country and language UI preference (specified in the URL). If the gadget can’t find an exact match, it “falls back” through the available message bundles to find the closest match.
With message fallback, you can have "default" messages (typically specified in ALL_ALL.xml), in case one isn’t available for a given locale.
For example, imagine that you have the following specified in your gadget:
<Locale messages="http://x.com/ALL_ALL.xml"/> <Locale lang="de" messages="http://x.com/de_ALL.xml"/> <Locale lang="de" country="DE" messages="http://x.com/de_DE.xml"/> <Locale lang="de" country="US" messages="http://x.com/de_US.xml"/>
How would these files be used? Suppose you have a user whose domain is the US (http://www.google.com), but whose language preference is German. She would see the messages from the de_US.xml file. If a particular message weren’t available from that file, she would see the message from de_ALL.xml, and finally, ALL_ALL.xml.
A user from Germany using the German UI would start with de_DE.xml, a user from Switzerland using the German UI would start with de_ALL.xml, and a user from Switzerland using the French UI would start with ALL_ALL.xml.
Accessing Messages from Message Bundles
How does a gadget access the appropriate message in a message bundle? Consider the de_ALL.xml (German language, country ALL) file in the “Hello World” example:
<messagebundle> <msg name="hello_world"> Hallo Welt. </msg> <msg name="color">Farbe</msg> <msg name="red">Rot</msg> <msg name="green">Grün</msg> <msg name="blue">Blau</msg> <msg name="gray">Grau</msg> <msg name="purple">Purpurrot</msg> <msg name="black">Schwarz</msg> </messagebundle>
Each message has a unique name string that identifies it. For example, in this message:
<msg name="red">Rot</msg>
the unique message name is “red” and the translated string that appears in the gadget (in its userprefs drop-down menu) is “Rot”, the German word for “red”. Here is the equivalent English message string in the message bundle ALL_ALL.xml:
<msg name="red">Red</msg>
The “Hello World” gadget spec uses the __MSG_
substitution
variable to indicate where the value from the appropriate
message bundle should be substituted. For example, this XML
statement in the “Hello World” gadget spec is used to add
a menu item to the userprefs “Color” drop-down menu:
<EnumValue value="Red" display_value="__MSG_red__" />
In effect this line says “Go to the message bundle that best matches
the user’s country/language profile, get the value for the
message named ‘red’, and substitute it in place of __MSG_red__
.”
This is how the user preferences drop-down menu in the “Hello
World” example is populated with color names (here shown in
German):
Displaying Messages in Gadget HTML
To display messages in the XML portion of your gadget, you use substitution variables, as discussed above. Within the CDATA portion of your gadget, you have a few different options.
The simplest technique is to embed the substitution variable in your HTML:
<b>__MSG_hello_world__</b>.
Alternatively, you can use the userprefs function getMsg()
. Note
that this function can be used to access all of the messages in
message bundles, not just the ones relating to userprefs. However,
you must call this function on a Prefs
object. For example:
var prefs = new gadgets.Prefs(); prefs.getMsg(“red”);
Hello World Gadget Spec
Here is the gadget spec for the "Hello World" example:
<?xml version="1.0" encoding="UTF-8" ?> <Module> <ModulePrefs title="i18n Example"> <Locale messages="http://doc.examples.googlepages.com/ALL_ALL.xml"/> <Locale lang="de" messages="http://doc.examples.googlepages.com/de_ALL.xml"/> <Locale lang="zh-cn" messages="http://doc.examples.googlepages.com/zh_cn_ALL.xml"/> <Locale lang="fr" messages="http://doc.examples.googlepages.com/fr_ALL.xml"/> <Locale lang="ja" messages="http://doc.examples.googlepages.com/ja_ALL.xml"/> <Locale lang="es" messages="http://doc.examples.googlepages.com/es_ALL.xml"/> <Locale lang="it" messages="http://doc.examples.googlepages.com/it_ALL.xml"/> <Locale lang="ru" messages="http://doc.examples.googlepages.com/ru_ALL.xml"/> </ModulePrefs> <UserPref name="fontcolor" display_name="__MSG_color__" default_value="Red" datatype="enum"> <EnumValue value="Red" display_value="__MSG_red__" /> <EnumValue value="Green" display_value="__MSG_green__" /> <EnumValue value="Blue" display_value="__MSG_blue__" /> <EnumValue value="Gray" display_value="__MSG_gray__" /> <EnumValue value="Purple" display_value="__MSG_purple__" /> <EnumValue value="Black" display_value="__MSG_black__" /> </UserPref> <Content type="html"> <![CDATA[ <div id="content_div"></div> <script type="text/javascript"> // Display message in the user's preferred language function displayMsg(){ var div = document.getElementById('content_div'); // Get userprefs var prefs = new gadgets.Prefs(); // Set font color to user's color choice div.style.color = prefs.getString("fontcolor"); // Display message var html = "<br><h1>"; // Use prefs.getMsg to go to the appropriate message bundle // and get the string associated with the "hello_world" message. html += prefs.getMsg("hello_world"); html += "</h1>"; div.innerHTML = html; } gadgets.util.registerOnLoadHandler(displayMsg); </script> ]]> </Content> </Module>
Creating Bi-directional Gadgets
You can use the gadgets BIDI (bi-directional) API to dynamically change the direction of a gadget. "Direction" refers to the direction in which content is displayed: left-to-right, or right-to-left. Note that this is different from alignment. For example, you could have a gadget with right-aligned English text, but the direction of the text itself would still be left-to-right.
The BIDI API makes it possible to write gadgets that support both right-to-left languages (such as Hebrew and Arabic), as well as left-to-right languages (such as English). The BIDI API includes the following:
- A
language_direction
attribute under the<Locale...>
tag. Its value can either be "rtl" (right-to-left) or "ltr" (left-to-right). You use this attribute to set the direction of the gadget. By default, gadgets have the direction left-to-right. - A set of
__BIDI_...
substitution variables whose values change depending on the direction of the gadget. For example, the variable__BIDI_DIR__
has the value "ltr" when the gadget is in left-to-right mode, and "rtl" when the gadget is in right-to-left mode.
In the simplest case, you can use <Locale... language_direction="rtl">
to statically set the direction for a gadget. For example,
this gadget only displays Hebrew text, so its direction is set to always
be right-to-left:
<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="כותרת two-כיוונית"
height="100">
<Locale lang="ALL" language_direction="rtl" />
</ModulePrefs>
<Content type="html"><![CDATA[
אני כותב מימין לשמאל ולא הייתי צריך להגדיר
dir=rtl בשביל זה!<br>
]]>
</Content>
</Module>
However, suppose you have a gadget that includes message
bundles for
both left-to-right and right-to-left languages. How would you design
your gadget to support both cases? Here is an example of a gadget that
dynamically changes direction according to which message
bundle gets loaded for a user's locale. This gadget has message
bundles for two right-to-left languages, Hebrew (iw
)
and Arabic (ar
):
<?xml version="1.0" encoding="UTF-8" ?> <Module> <ModulePrefs title="BIDI Hello World"> <Locale messages="http://doc.examples.googlepages.com/ALL_ALL.xml"/> <Locale lang="ru" messages="http://doc.examples.googlepages.com/ru_ALL.xml"/> <Locale lang="fr" messages="http://doc.examples.googlepages.com/fr_ALL.xml"/> <Locale lang="ja" messages="http://doc.examples.googlepages.com/ja_ALL.xml"/> <Locale lang="es" messages="http://doc.examples.googlepages.com/es_ALL.xml"/> <Locale lang="it" messages="http://doc.examples.googlepages.com/it_ALL.xml"/> <Locale lang="iw" messages="http://doc.examples.googlepages.com/iw_ALL.xml" language_direction="rtl"/> <Locale lang="ar" messages="http://doc.examples.googlepages.com/ar_ALL.xml" language_direction="rtl"/> </ModulePrefs> <Content type="html"> <![CDATA[ <div style="margin-__BIDI_START_EDGE__:30px;"><h2>__MSG_hello_world__</h2></div> ]]> </Content> </Module>
For this gadget, suppose the user's language preference is Arabic.
The line <Locale
lang="ar" messages="http://.../ar_ALL.xml" language_direction="rtl"/>
says
"for Arabic, display the text
in the message bundle ar_ALL.xml, and set the gadget
direction to rtl." The direction would also be
right-to-left for Hebrew. For the other languages, the gadget direction
is implicitly set to the default ltr (left-to-right).
The line <div style="margin-__BIDI_START_EDGE__:30px;"><h2>__MSG_hello_world__</h2></div>
says
"when the gadget is in left-to-right mode, offset
the text from the left margin by 30 pixels. When the
gadget is in right-to-left mode,
offset the text from the right margin by 30 pixels."
For example, if the user's browser setting is Arabic (right-to-left),
this is how the substitution is done at run-time:
<div style="margin-right:30px;"><h2>أهلاً بالعالم</h2></div>
This table lists the BIDI substitution variables and their possible
values. Note that their values are always relative to the direction
of the gadget itself. In other words, these variables are designed
to be used in conjunction with the <Locale... language_direction="...">
setting.
You can use these variables anywhere within the body of the gadget.
Variable | Description |
---|---|
__BIDI_START_EDGE__ |
This variable represents the side of the gadget that is the starting point for content display. The value is "left" when the gadget is in LTR-mode and "right" when the gadget is in RTL-mode. The above gadget gives an example of using this variable for CSS margin settings. |
__BIDI_END_EDGE__ |
This variable represents the edge of the gadget that is opposite to where content display starts. The value is "right" when the gadget is in LTR-mode and "left" when the gadget is in RTL-mode. |
__BIDI_DIR__ |
The value of this variable is "ltr" when the gadget is in LTR-mode and "rtl" when the gadget is in RTL-mode. |
__BIDI_REVERSE_DIR__ |
The value of this variable is "rtl" when the gadget is in LTR-mode and "ltr" when the gadget is in RTL-mode. |
Changing Direction for Part of a Gadget
The BIDI API is designed to change the direction of an entire gadget, and to format the gadget's content accordingly. However, there may be cases where you want to tweak the direction of individual lines of text within a gadget. You may not want the gadget direction to be tied to the user's language setting, or to any particular language. For example, you might create a gadget that simultaneously displays English and Hebrew text. Or, you might create a gadget that lists common phrases for a variety of languages. You can use regular DOM function calls and CSS to set text direction within the body of a gadget. For example:
<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="DOM Hello World" height="200" />
<Content type="html"><![CDATA[
<script type="text/javascript">
// In this gadget, users choose a language from a drop-down menu, and the gadget displays
// a "Hello World" message for the selected language. The gadget uses DOM functions to set
// the direction and formatting for the message, depending on whether its language is RTL
// or LTR.
// Associative array containing "Hello World" messages for different languages
var msgs = new Object();
msgs = {
"English" : "Hello World",
"Hebrew" : "שלום עולם",
"French" : "Bonjour Monde",
"Arabic" : "أهلاً بالعالم",
"Russian" : "Здравствуйте Мир!"
};
function showMsg() {
var html="<h1>";
var div = _gel('mydiv');
div.style.color = "green";
var index = document.myform.Language.selectedIndex;
var lang = document.myform.Language.options[index].text;
var str = msgs[lang];
if (!str)
str="";
// If language is Hebrew or Arabic, set the div direction to be right-to-left.
// Offset text 30px from right margin.
if(lang=="Hebrew" || lang=="Arabic") {
div.style.direction = "rtl";
div.style.marginRight = "30px";
html += str;
}
// For other languages, set div direction to left-to-right.
// Offset text 30px from left margin.
else {
div.style.direction = "ltr";
div.style.marginLeft = "30px";
html += str;
}
html+= "</h1>";
div.innerHTML = html;
}
</script>
<div style="background-color: #BFCFFF; height: 200px; color:green;">
<br />
<div>
<form name="myform" style="text-align: center;">
<select name="Language" onchange="showMsg()">
<option>Pick a Language
<option>English
<option>Hebrew
<option>French
<option>Arabic
<option>Russian
</select>
</form>
</div>
<br />
<div id="mydiv"><h2 style='text-align: center;'>****Pick a language****</h2></div>
</div>
]]>
</Content>
</Module>
Parsing and Formatting International Content
You can use the gadgets.i18n.*
JavaScript API to parse and
format international numbers and datetimes. For details, see the JavaScript
API Reference.
Testing
To test your gadget, you can change your country and language settings to correspond to the message bundles your gadget includes.
The simplest way to change your country and language settings in iGoogle is to edit the URL:
http://www.google.com/ig?gl=<country>&hl=<lang>
For example, in the following URL, the country is Germany (DE) and the language is English (en). Typically, country takes precedence over language, so with this URL, iGoogle displays text in German:
http://www.google.com/ig?gl=DE&hl=en
In this example the country is implicitly the US, and the language is Spanish:
http://www.google.com/ig?hl=es
You can use this link to help construct the correct URL for a particular language: http://www.google.com/help/customize.html#searchlang. The valid values for language are ISO639-1 two-digit language codes, and for country they are ISO 3166-1 alpha-2 codes.
Caching
Message bundles have the same caching behavior as for gadget specs themselves: we typically refresh the cache every 1-2 hours, hosting servers should see only ~100-200 requests per day for message bundles, and if they're unavailable, we'll continue using our "stale" copy for as long as we can. In the developer.xml gadget, if you disable caching for a gadget, it also disables caching for the gadget’s message bundles.
Troubleshooting
During development, you might find that your gadget either comes up blank or displays ??? where a message should be. The problem could be any of the following:
- You didn’t save the message bundle as UTF-8. This is necessary for any non-English language files that include special characters.
- There is a problem with your message bundle syntax.
- You don’t have an ALL_ALL.xml message file for your gadget to use by default.
- In your gadget, the URL for the message bundle is incorrect.
- There is a mismatch between the message bundle and the message substitution variable you specified in your gadget.